package com.derbysoft.synxisadapter.dswitch2.service.impl;

import com.derbysoft.synxisadapter.commons.dto.synxis.OTACancelRQ;
import com.derbysoft.synxisadapter.commons.dto.synxis.OTACancelRS;
import com.derbysoft.synxisadapter.commons.dto.synxis.OTAHotelAvailRQ;
import com.derbysoft.synxisadapter.commons.exception.NetErrorException;
import com.derbysoft.synxisadapter.commons.model.Channel;
import com.derbysoft.synxisadapter.dswitch2.service.ProviderWebService;
import com.derbysoft.synxisadapter.webservice.SynXisWebService;
import com.derbysoft.synxisadapter.webservice.impl.ResponsePair;
import org.apache.commons.lang.StringUtils;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.xml.transform.StringSource;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ProviderWebServiceImpl implements ProviderWebService {
    protected WebServiceTemplate bookWebServiceTemplate;
    protected WebServiceTemplate availWebServiceTemplate;
    private SynXisWebService<OTACancelRQ, OTACancelRS> cancellationWebService;

    private static Map<String, String> headerMap = new ConcurrentHashMap<String, String>();

    private static final String HEADER_TEMPLATE = "<HTNGHeader xmlns=\"http://htng.org/1.1/Header/\"><From><Credential>" +
            "<userName>${userName}</userName><password>${password}</password></Credential></From></HTNGHeader>";

    private static final String CANCEL_ACTION = "http://synxis.com/OTA2004AService/CancelReservations";

    @Override
    public List<ResponsePair<OTACancelRQ, OTACancelRS>> batchCancel(List<OTACancelRQ> cancelRQs, Channel channel) throws Exception {
        return cancellationWebService.batchInvoke(cancelRQs, channel);
    }

    @Override
    public Object invoke(final Object rq, final Channel channel) {
        try {
            return findWebServiceTemplate(rq).marshalSendAndReceive(rq, new WebServiceMessageCallback() {
                @Override
                public void doWithMessage(WebServiceMessage webServiceMessage) throws IOException, TransformerException {
                    SoapMessage soapMessage = (SoapMessage) webServiceMessage;

                    SoapHeader soapHeader = soapMessage.getSoapHeader();

                    if (rq instanceof OTACancelRQ) {
                        soapMessage.setSoapAction(CANCEL_ACTION);
                    }

                    Transformer transformer = TransformerFactory.newInstance().newTransformer();
                    transformer.transform(new StringSource(createHeaderString(channel)), soapHeader.getResult());
                }
            });
        } catch (Exception e) {
            throw new NetErrorException(e.getMessage());
        }
    }

    private String createHeaderString(Channel channel) {
        String key = makeHeaderKey(channel);
        if (headerMap.containsKey(key)) {
            return headerMap.get(key);
        }

        String result = HEADER_TEMPLATE;

        result = StringUtils.replace(result, "${userName}", channel.getUserName());
        result = StringUtils.replace(result, "${password}", channel.getPassword());

        headerMap.put(key, result);

        return result;
    }

    private String makeHeaderKey(Channel channel) {
        return channel.getUserName() + channel.getPassword();
    }

    private WebServiceTemplate findWebServiceTemplate(Object rq) {
        return OTAHotelAvailRQ.class.isInstance(rq) ? availWebServiceTemplate : bookWebServiceTemplate;
    }

    public void setBookWebServiceTemplate(WebServiceTemplate bookWebServiceTemplate) {
        this.bookWebServiceTemplate = bookWebServiceTemplate;
    }

    public void setAvailWebServiceTemplate(WebServiceTemplate availWebServiceTemplate) {
        this.availWebServiceTemplate = availWebServiceTemplate;
    }

    public void setCancellationWebService(SynXisWebService<OTACancelRQ, OTACancelRS> cancellationWebService) {
        this.cancellationWebService = cancellationWebService;
    }
}
